package cn.initcap.algorithm.swing.fractal.snowflake;

import cn.initcap.algorithm.swing.AlgoVisHelper;
import cn.initcap.algorithm.swing.BaseAlgoFrame;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

/**
 * @author initcap
 * @date 8/1/19 7:08 PM
 */
public class AlgoVisualizer {

    private static int DELAY = 40;
    private FractalData data;
    private BaseAlgoFrame frame;

    public AlgoVisualizer(int depth, int side) {

        data = new FractalData(depth);
        int sceneWidth = side;
        int sceneHeight = side / 3;

        EventQueue.invokeLater(() -> {
            frame = new BaseAlgoFrame("Fractal Visualizer", sceneWidth, sceneHeight) {
                @Override
                public void draw(Graphics graphics) {
                    Graphics2D g2d = (Graphics2D) graphics;
                    drawFractal(g2d, 0, sceneHeight - 3, sceneWidth, 0, 0);
                }
            };
            frame.addKeyListener(new AlgoKeyListener());
            new Thread(() -> {
                run();
            }).start();
        });
    }

    public static void main(String[] args) {

        int depth = 6;
        int side = 900;

        new AlgoVisualizer(depth, side);
    }

    private void run() {

        setData(data.depth);
    }

    private void setData(int depth) {
        data.depth = depth;

        frame.render(data);
        AlgoVisHelper.pause(DELAY);
    }

    public void addAlgoKeyListener() {
        frame.addKeyListener(new AlgoKeyListener());
    }

    private void drawFractal(Graphics2D g, double x1, double y1, double side, double angle, int depth) {

        if (side <= 0) {
            return;
        }

        if (depth == data.depth) {
            double x2 = x1 + side * Math.cos(angle * Math.PI / 180.0);
            double y2 = y1 - side * Math.sin(angle * Math.PI / 180.0);
            AlgoVisHelper.setColor(g, AlgoVisHelper.INDIGO);
            AlgoVisHelper.drawLine(g, x1, y1, x2, y2);
            return;
        }

        double side3 = side / 3;

        double x2 = x1 + side3 * Math.cos(angle * Math.PI / 180.0);
        double y2 = y1 - side3 * Math.sin(angle * Math.PI / 180.0);
        drawFractal(g, x1, y1, side3, angle, depth + 1);

        double x3 = x2 + side3 * Math.cos((angle + 60.0) * Math.PI / 180.0);
        double y3 = y2 - side3 * Math.sin((angle + 60.0) * Math.PI / 180.0);
        drawFractal(g, x2, y2, side3, angle + 60.0, depth + 1);

        double x4 = x3 + side3 * Math.cos((angle - 60.0) * Math.PI / 180.0);
        double y4 = y3 - side3 * Math.sin((angle - 60.0) * Math.PI / 180.0);
        drawFractal(g, x3, y3, side3, angle - 60.0, depth + 1);

        drawFractal(g, x4, y4, side3, angle, depth + 1);
    }

    private class AlgoKeyListener extends KeyAdapter {

        @Override
        public void keyReleased(KeyEvent event) {
            //System.out.println("Key released:" + event);
            if (event.getKeyChar() >= '0' && event.getKeyChar() <= '9') {
                int depth = event.getKeyChar() - '0';
                setData(depth);
            }
        }
    }
}
